package com.tmt.im.common.store;

import com.tmt.im.common.annotation._AllowUpdate;
import com.tmt.im.common.pojo.IGetKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;

/**
 * 考虑到性能问题,没有使用本类.
 *
 * @Description TODO
 * @Author wuyi
 * @Date 2024/8/14 9:24
 * @Version 1.0
 **/
@Slf4j
public class StoreSqlHelper {

    /**
     * 获取初始化IgniteCache
     *
     * @param pojoClass
     * @param <K>
     * @param <T>
     * @return
     */
    public static <K, T extends IGetKey<K>> LinkedHashMap<String, String> getFieldDefineList(Class<T> pojoClass) {
        LinkedHashMap<String, String> fields = new LinkedHashMap<>();
        Arrays.stream(pojoClass.getDeclaredFields())
                .filter(x -> !Modifier.isStatic(x.getModifiers())
                        && x.getAnnotation(QuerySqlField.class) != null).forEach(x -> {
                    String name = x.getType().getSimpleName();
                    if (int.class.getSimpleName().equals(name)) {
                        name = Integer.class.getName();
                    } else if (boolean.class.getSimpleName().equals(name)) {
                        name = Boolean.class.getName();
                    } else if (long.class.getSimpleName().equals(name)) {
                        name = Long.class.getName();
                    } else {
                        name = x.getType().getName();
                    }
                    fields.put(x.getName(), name);
                });
        return fields;
    }

    /**
     * 根据Pojo类生成CacheStore类需要的SQL语句.
     *
     * @param action    操作类型,有效值: select insert delete
     * @param pojoClass
     * @param <K>
     * @param <T>
     * @return
     */
    public static <K, T extends IGetKey<K>> String generateSqlWithParams(String action, Class<T> pojoClass) {
        String tableName = getTableName(pojoClass);
        if ("delete".equalsIgnoreCase(action)) {
            return MessageFormat.format("delete from {0} where id = ?", tableName);
        }

        //获取数据表列名称列表
        Field[] fields = pojoClass.getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        StringBuilder allowUpdates = new StringBuilder();
        StringBuilder valBuilder = new StringBuilder();
        Arrays.stream(fields).filter(x -> !Modifier.isStatic(x.getModifiers()) && !x.getName().equals("mid"))
                .forEach(x -> {
                    QuerySqlField ann = x.getAnnotation(QuerySqlField.class);
                    if (ann == null) {
                        return;
                    }
                    String columnName = StringUtils.hasText(ann.name()) ? ann.name() : x.getName();
                    sb.append(",").append(columnName);
                    valBuilder.append(",").append("?");
                    if (null != x.getAnnotation(_AllowUpdate.class)) {
                        allowUpdates.append(",").append(MessageFormat.format("{0} = ?", columnName));
                    }
                });

        String result = null;
        switch (action) {
            case "select" -> {
                if (sb.length() > 0) {
                    result = MessageFormat.format("select {0} from {1} where id = ?", sb.substring(1), tableName);
                }
            }
            case "insert" -> {
                if (sb.length() > 0) {
                    result = MessageFormat.format("insert into {0} ({1}) VALUES ({2})", tableName, sb.substring(1), valBuilder.substring(1));
                }
            }
            case "update" -> {
                if (allowUpdates.length() > 0) {
                    result = MessageFormat.format("update {0} set {1} where id = ?", tableName, allowUpdates.substring(1));
                }
            }
            default -> {
            }
        }
        return result;
    }

    public static <K, T extends IGetKey<K>> void initialObject(ResultSet rs, T pojo) throws Exception {
        Field[] fields = pojo.getClass().getDeclaredFields();
        List<Field> validFields = Arrays.stream(fields).filter(x -> !Modifier.isStatic(x.getModifiers())).toList();
        for (Field validField : validFields) {
            QuerySqlField ann = validField.getAnnotation(QuerySqlField.class);
            if (ann == null) {
                continue;
            }
            String columnName = StringUtils.hasText(ann.name()) ? ann.name() : validField.getName();
            validField.setAccessible(true);

            String name = validField.getType().getSimpleName();
            if (Integer.class.getSimpleName().equals(name)
                    || Long.class.getSimpleName().equals(name)
                    || Boolean.class.getSimpleName().equals(name)) {
                name = "Object";
            } else
                if (int.class.getSimpleName().equals(name)) {
                name = "Int";
            } else if (boolean.class.getSimpleName().equals(name)) {
                name = "Boolean";
            } else if (long.class.getSimpleName().equals(name)) {
                name = "Long";
            }

            String methodName = MessageFormat.format("get{0}", name);
            Method method = ResultSet.class.getMethod(methodName, String.class);
            Object value = method.invoke(rs, columnName);
            validField.set(pojo, value);
        }
    }

    public static <K, T extends IGetKey<K>> void initPreparedStatement(PreparedStatement st, T pojo) throws Exception {
        Field[] fields = pojo.getClass().getDeclaredFields();
        List<Field> validFields = Arrays.stream(fields).filter(x -> !Modifier.isStatic(x.getModifiers())
                && !x.getName().equals("mid")).toList();
        int index = 1;
        for (int i = 0; i < validFields.size(); i++) {
            Field validField = validFields.get(i);
            if (!pojo.isInsert()) {
                _AllowUpdate ann = validField.getAnnotation(_AllowUpdate.class);
                if (ann == null) {
                    continue;
                }
            }
            QuerySqlField ann = validField.getAnnotation(QuerySqlField.class);
            if (ann == null) {
                continue;
            }
            validField.setAccessible(true);

            String name = validField.getType().getSimpleName();
            if (int.class.getSimpleName().equals(name) || Integer.class.getSimpleName().equals(name)) {
                name = "Int";
            } else if (boolean.class.getSimpleName().equals(name) || Boolean.class.getSimpleName().equals(name)) {
                name = "Boolean";
            } else if (long.class.getSimpleName().equals(name) || Long.class.getSimpleName().equals(name)) {
                name = "Long";
            }

            Class<?> paramType = validField.getType();
            if (Objects.equals(validField.getType(), Long.class)) {
                paramType = long.class;
            } else if (Objects.equals(validField.getType(), Boolean.class)) {
                paramType = boolean.class;
            } else if (Objects.equals(validField.getType(), Integer.class)) {
                paramType = int.class;
            }

            String methodName = MessageFormat.format("set{0}", name);
            Method method = st.getClass().getMethod(methodName, int.class, paramType);
            if (validField.get(pojo) == null) {
                method = st.getClass().getMethod("setObject", int.class, Object.class);
                method.invoke(st, index, null);
            } else {
                method.invoke(st, index, validField.get(pojo));
            }
            index++;
        }

        if (!pojo.isInsert()) {
            try {
                Method method = pojo.getClass().getMethod("getId");
                st.setLong(index, (Long) method.invoke(pojo));
            } catch (Exception ex) {
                log.error("读取POJO对象的ID出现异常", ex);
            }
        }
    }

    /**
     * 属性列表转对象列表
     *
     * @param cursor
     * @param objectList
     * @param clazz
     * @param <K>
     * @param <T>
     */
    public static <K, T extends IGetKey<K>> void convertTo(FieldsQueryCursor<List<?>> cursor, List<T> objectList, Class<T> clazz) {
        assert cursor != null;
        List<List<?>> rows = cursor.getAll();
        Field[] fields = clazz.getDeclaredFields();
        for (List<?> row : rows) {
            try {
                T object = clazz.getDeclaredConstructor().newInstance();
                object.setInsert(false);
                objectList.add(object);
                Method keyMethod = object.getClass().getMethod("getKey");
                Class<?> keyType = keyMethod.getReturnType();
                for (int i = 0; i < row.size(); i++) {
                    try {
                        String columnName = cursor.getFieldName(i);
                        Field field = Arrays.stream(fields).filter(x -> x.getName().equalsIgnoreCase(columnName))
                                .findFirst().orElse(null);
                        if (field != null) {
                            field.setAccessible(true);
                            Object val = row.get(i);
                            try {
                                field.set(object, val);
                            } catch (Exception ex) {
                                if (field.getName().equals("id")) {
                                    Method method = val.getClass().getMethod("getId");
                                    Object v = method.invoke(val);
                                    field.set(object, v);
                                } else {
                                    log.error(ex.getMessage(), ex);
                                }
                            }
                        }
                    } catch (Exception ignored) {
                    }
                }
            } catch (Exception ex) {
                log.error("属性值列表转对象时出现异常", ex);
            }
        }
    }

    /*******************************私有方法******************************************/

    private static <K, T extends IGetKey<K>> String getTableName(Class<T> pojoClass) {
        String tableName = null;
        try {
            Field field = pojoClass.getDeclaredField("CACHE_NAME");
            field.setAccessible(true);
            tableName = (String) field.get(pojoClass);
        } catch (Exception ex) {
            log.error("{}类没有定义标识数据表名称的静态字段CACHE_NAME", pojoClass.getSimpleName());
        }
        return tableName;
    }


}
